package com.gupaoedu.vip.pattern.springmvc2.aop.support;

import com.gupaoedu.vip.pattern.springmvc2.aop.aspect.MyMethodAfterAdviceInterceptor;
import com.gupaoedu.vip.pattern.springmvc2.aop.aspect.MyMethodAfterThrowAdviceInterceptor;
import com.gupaoedu.vip.pattern.springmvc2.aop.aspect.MyMethodBeforeAdviceInterceptor;
import com.gupaoedu.vip.pattern.springmvc2.aop.config.MyAopConfig;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyAdvisedSupport {

    private Class<?> targetClass;

    private Object target;

    private MyAopConfig config;

    private Pattern pointCutClassPattern;

    private Map<Method,List<Object>> methodCache;

    public MyAdvisedSupport(MyAopConfig config) {
        this.config = config;
    }

    public Class<?> getTargetClass(){
        return this.targetClass;
    }

    public Object getTarget(){
        return this.target;
    }

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method,Class<?> targetClass) throws NoSuchMethodException {
        List<Object> chain = this.methodCache.get(method);
        if(chain == null){
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            chain = this.methodCache.get(m);
            this.methodCache.put(m, chain);
        }
        return chain;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    private void parse() {
        String pointCut = this.config.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");
        String poinCutForClass = pointCut.substring(0,pointCut.lastIndexOf("\\(")-4);
        pointCutClassPattern = Pattern.compile("class " +
                poinCutForClass.substring(poinCutForClass.lastIndexOf(" ")+1));
        try {
            Pattern pattern = Pattern.compile(pointCut);
            methodCache = new HashMap<Method, List<Object>>();
            Class<?> aspectClass = Class.forName(this.config.getAspectClass());
            Map<String, Method> map = new HashMap<String, Method>();
            for (Method method : aspectClass.getMethods()) {
                map.put(method.getName(), method);
            }

            for (Method method : this.targetClass.getMethods()) {
                String methodString = method.toString();
                if(methodString.contains("throws")){
                    methodString = methodString.substring(0,methodString.lastIndexOf("throws")).trim();
                }
                Matcher matcher = pattern.matcher(methodString);
                if(matcher.matches()){
                    List<Object> chain = new LinkedList<Object>();
                    if(!(null==this.config.getAspectBefore()||"".equals(this.config.getAspectBefore()))){
                        chain.add(new MyMethodBeforeAdviceInterceptor(map.get(this.config.getAspectBefore()), aspectClass.newInstance()));
                    }
                    if(!(null==this.config.getAspectAfter()||"".equals(this.config.getAspectAfter()))){
                        chain.add(new MyMethodAfterAdviceInterceptor(map.get(this.config.getAspectAfter()), aspectClass.newInstance()));
                    }
                    if(!(null==this.config.getAspectAfterThrow()||"".equals(this.config.getAspectAfterThrow()))){
                        chain.add(new MyMethodAfterThrowAdviceInterceptor(map.get(this.config.getAspectAfterThrow()), aspectClass.newInstance()));
                    }
                    this.methodCache.put(method, chain);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public boolean pointMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }
}
